package com.yycx.module.file.provider.oss.client;

import cn.hutool.json.JSONUtil;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.*;
import com.yycx.common.base.entity.EntityMap;
import com.yycx.common.base.utils.FlymeUtils;
import com.yycx.common.constants.SettingConstant;
import com.yycx.common.utils.ApiAssert;
import com.yycx.common.utils.RedisUtils;
import com.yycx.module.file.client.entity.SysFile;
import com.yycx.module.file.client.vo.OssSetting;
import com.yycx.module.file.client.vo.UploadRequest;
import com.yycx.module.file.provider.enums.StoreTypeEnum;
import com.yycx.module.file.provider.service.OssUploadService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 亚马逊文件上传
 */
@Component("AWS_OSS")
@Slf4j
public class AwsOssClient implements OssUploadService {


    /**
     * 存放合并信息集合 如果服务是集群或多活部署，需改成redis存储
     */
    private final static Map<String, Map<String, Object>> uploadIdMap = new ConcurrentHashMap<>();

    private RedisUtils redisUtils;

    private AmazonS3 s3client;

    private String bucketName;


    @PostConstruct
    private void init() {
        OssSetting ossSetting = getOssSetting();
        if (FlymeUtils.isNotEmpty(ossSetting)) {
            String used = redisUtils.getConfig(SettingConstant.OSS_USED);
            if (FlymeUtils.isNotEmpty(ossSetting) && used.equals(StoreTypeEnum.AWS_OSS.name())) {
                this.bucketName = ossSetting.getBucket();
                s3client = getAmazonS3(ossSetting);
            }
        }
    }

    @Override
    public OssSetting getOssSetting() {
        String v = redisUtils.getConfig(StoreTypeEnum.AWS_OSS.name());
        if (FlymeUtils.isNotEmpty(v)) {
            return JSONUtil.toBean(v, OssSetting.class);
        }
        return null;
    }

    @Override
    public String getBaseOssPath() {
        OssSetting ossSetting = getOssSetting();
        String ossBasePath = ossSetting.getHttp() + ossSetting.getBucket() + "." + ossSetting.getEndpoint() + "/";
        return ossBasePath;
    }

    @Override
    public String getOssPath(String fileBasePath, String fileName) {
        return getBaseOssPath() + fileBasePath + fileName;
    }

    @Override
    public String getLocalPath(String fileBasePath, String fileKey) {
        return getOssSetting().getFilePath() + "/" + fileBasePath+fileKey;
    }

    public AmazonS3 getAmazonS3(OssSetting ossSetting) {
        String bucketRegion = ossSetting.getBucketRegion();
        AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard().withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials(ossSetting.getAccessKey(), ossSetting.getSecretKey())));
        if (FlymeUtils.isNotEmpty(ossSetting.getEndpoint())) {
            builder.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(ossSetting.getEndpoint(), bucketRegion));
        } else if (FlymeUtils.isNotEmpty(bucketRegion)) {
            builder.withRegion(bucketRegion).withForceGlobalBucketAccessEnabled(true);
        }
        return builder.build();
    }

    /**
     * 关闭
     */
    public void shutdown(AmazonS3 s3) {
        if (s3 != null) {
            s3.shutdown();
        }
    }


    /**
     * 上传文件
     *
     * @param filePath
     * @param fileBasePath
     * @param fileKey
     * @throws Exception
     */
    @Override
    public String upload(String filePath, String fileName, SysFile sysFile, Long uid, String fileBasePath, String fileKey, Long userId, EntityMap params) {
        String objectID = fileBasePath + fileKey;
        try {
            File file = new File(filePath);
            PutObjectRequest request = new PutObjectRequest(bucketName, objectID, file);
            s3client.putObject(request);
        } catch (Exception e) {
            s3client.deleteObject(bucketName, objectID);
            throw e;
        } finally {
            shutdown(s3client);
        }
        return objectID;
    }


    /**
     * 分段上传文件
     *
     * @param upload
     * @return
     */
    public String mutilpartUpload(UploadRequest upload, String filePath, String fileBasePath, String fileKey) {
        log.info("开始上传");
        if (FlymeUtils.isEmpty(s3client)) {
            ApiAssert.failure("您还未配置亚马逊OSS");
        }
        InputStream inputStream = null;
        String objectID = fileBasePath + fileKey;
        try {
            String uploadId = null;
            List<PartETag> partETagList = new ArrayList<>();
            Map<String, Object> result = uploadIdMap.get(upload.getIdentifier());
            if (result == null) {
                InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(bucketName, objectID);
                InitiateMultipartUploadResult initResponse = s3client.initiateMultipartUpload(initRequest);
                uploadId = initResponse.getUploadId();
                result = new HashMap<>(5);
                result.put("uploadId", uploadId);
                result.put("partETagList", partETagList);
                result.put("fileKey", objectID);
            } else {
                uploadId = result.get("uploadId").toString();
                partETagList = (List<PartETag>) result.get("partETagList");
                fileKey = result.get("fileKey").toString();
            }
            File file = new File(filePath);
            Long fileSize = file.length();
            // 分段上传
            inputStream = new FileInputStream(file);
            UploadPartRequest uploadRequest = new UploadPartRequest()
                    .withBucketName(upload.getBucketName())
                    .withKey(objectID)
                    .withUploadId(uploadId)
                    .withPartNumber(upload.getChunkNumber())
                    .withInputStream(inputStream)
                    .withPartSize(fileSize);
            UploadPartResult uploadResult = s3client.uploadPart(uploadRequest);

            partETagList.add(uploadResult.getPartETag());

            log.info("上传完成: upload.getTotalChunks()：{}, upload.getChunkNumber()：{}", upload.getTotalChunks(), upload.getChunkNumber());
            if (upload.getTotalChunks() == upload.getChunkNumber()) {
                log.info("开始合并");
                CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(upload.getBucketName(), fileKey, uploadId, partETagList);
                s3client.completeMultipartUpload(compRequest);
                // 删除map数据
                uploadIdMap.remove(upload.getIdentifier());
            } else {
                // 更新map数据
                uploadIdMap.put(upload.getIdentifier(), result);
            }
            return objectID;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (Exception e) {

                }
            }
        }
        return objectID;
    }

    /**
     * 下载
     *
     * @param bucketName
     * @param fileKey
     * @return
     * @throws Exception
     */
    public InputStream downloadFile(String bucketName, String fileKey) throws Exception {
        GetObjectRequest request = new GetObjectRequest(bucketName, fileKey);
        S3Object response = s3client.getObject(request);
        InputStream input = response.getObjectContent();
        return input;
    }

    /**
     * 删除文件
     *
     * @param bucketName
     * @param fileKey
     * @throws Exception
     */
    public void deleteFile(String bucketName, String fileKey) throws Exception {
        DeleteObjectRequest request = new DeleteObjectRequest(bucketName, fileKey);
        s3client.deleteObject(request);
    }

    /**
     * Bucket列表
     *
     * @return
     * @throws Exception
     */
    public List<Bucket> listFile() throws Exception {
        ListBucketsRequest request = new ListBucketsRequest();
        List<Bucket> list = s3client.listBuckets(request);
        return list;
    }

    /**
     * 是否存在Bucket
     *
     * @param bucketName
     * @return
     * @throws Exception
     */
    public boolean isExistBucket(String bucketName) throws Exception {
        try {
            HeadBucketRequest request = new HeadBucketRequest(bucketName);
            s3client.headBucket(request);
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 创建Bucket
     *
     * @param bucketName
     * @return
     * @throws Exception
     */
    public void createBucket(String bucketName) throws Exception {
        boolean isBucketExists = isExistBucket(bucketName);
        if (!isBucketExists) {
            CreateBucketRequest request = new CreateBucketRequest(bucketName);
            s3client.createBucket(request);
        }
    }

    /**
     * 删除Bucket
     *
     * @param bucketName
     * @return
     * @throws Exception
     */
    public void deleteBucket(String bucketName) throws Exception {
        DeleteBucketRequest request = new DeleteBucketRequest(bucketName);
        s3client.deleteBucket(request);
    }

    public AwsOssClient(RedisUtils redisUtils) {
        this.redisUtils = redisUtils;
    }

}
